package com.jweb.framework.ws.sdo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.TransformerException;

import org.apache.tuscany.sdo.api.SDOUtil;
import org.apache.tuscany.sdo.api.XMLStreamHelper;
import org.apache.tuscany.sdo.util.resource.XMLDocumentStreamReader;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.xml.xsd.XsdSchema;

import com.jweb.framework.core.util.XMLUtils;
import com.jweb.framework.ws.support.CommonsXsdSchemaJWeb;

import commonj.sdo11.DataObject;
import commonj.sdo11.Type;
import commonj.sdo11.helper.HelperContext;
import commonj.sdo11.helper.XMLDocument;
/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：SDO解析工具类
 */
public class SdoUtils {
	
	private static Logger logger = LoggerFactory.getLogger(SdoUtils.class);
	
	private static boolean flag = true ;
	
	private static HelperContext helperContext = SDOUtil.createHelperContext(flag);
	
	private static Map<String, HelperContext> mapD = new HashMap<String, HelperContext>();
	private static Map<String , Map<QName, QName>> mapE = new HashMap<String, Map<QName,QName>>();
	
	
	private static QName invoke(String str, QName qname) {
		Map map = mapE.get(str);
		if(map == null){
			return qname ;
		}
		QName localeQName = (QName) map.get(qname);
		if(localeQName == null){
			return qname ;
		}
		
		return localeQName ;
	}
	/*
	 * HelperContext 提供了一套SDO助手类实例的访问方法。它描述一个助手执行上下文。
	 * 		该接口中的方法返回的助手集对相同的SDO元数据都具有可见性，即它们在相同的"scope"中执行。
	 */
	static HelperContext invoke(String paramStr , boolean flag) {
		
		if(paramStr == null){
			return helperContext ;
		}
		// intern()：返回字符串对象的规范化表示形式。
		String str = paramStr.intern();
		synchronized(str){
			HelperContext localHelperContext = mapD.get(str);
			if(localHelperContext == null){
				if(flag){
					localHelperContext = SDOUtil.createHelperContext(flag);
					logger.debug("new scoped sdo HelperContext for {}" , str);
					//logger.debug("new scoped sdo HelperContext for" );
					mapD.put(str, localHelperContext);
				}
				else {
					// test start
					//throw new IllegalStateException("no helper context found for scope: " + paramStr);
					
					localHelperContext = SDOUtil.createHelperContext(true);
					// test end 
				}
			}
			return localHelperContext ;
		}
	}
	
	private static QName invoke(String str , QName qname1 , QName qname2){
		Object obj = mapE.get(str);
		if(obj == null){
			obj = new HashMap();
			mapE.put(str, (Map<QName, QName>) obj);
		}
		return (QName) ((Map)obj).put(qname1, qname2);
	}
	
	
	private static void invoke(String str, XsdSchema[] xsdSchemas) throws TransformerException {
		
		for(int i = 0 ; i < xsdSchemas.length ; ++i){
			
			CommonsXsdSchemaJWeb commonsXsdSchemaJKurrent = (CommonsXsdSchemaJWeb) xsdSchemas[i];
			XmlSchema xmlSchema = commonsXsdSchemaJKurrent.getSchema();
			
			Iterator it = xmlSchema.getItems().getIterator();
			
			while(it.hasNext()){
				Object obj = it.next() ;
				if(obj instanceof XmlSchemaElement){
					XmlSchemaElement xmlSchemaElement = (XmlSchemaElement) obj ;
					QName qname1 = xmlSchemaElement.getSchemaTypeName() ; 
					if(qname1 != null){
						QName qname2 = invoke(str , xmlSchemaElement.getQName() , qname1);
						if((qname2!=null) && (!qname1.equals(qname2))){
							logger.debug("element:{} re-mapping to type:{} , original type:{}" , new Object[]{xmlSchemaElement.getQName() , qname1 , qname2});
						}
						else {
							logger.debug("element: {} mapping to type: {}" , xmlSchemaElement.getQName() , qname1);
						}
					}
				}
			}
		}
	}
	
	/*
	 * Type : A representation of the type of a property of data object .
	 */         // str="AntiCounterfeitRsltRecvSrv"	, weatherServicePortTypeService
	public static List<Type> defineSdoTypes(String str , XsdSchema[] xsdSchemas) throws TransformerException {
		
		ArrayList arrayList = new ArrayList();
		
		for(int i = xsdSchemas.length-1 ; i >= 0 ; i--){
			/*
			 * getXSDHelper(): Gets the XSDHelper to use in this context .
			 * XSDHelper : Provides access to additional information when the Type or Property is defined by an XML Schema(XSD). 
			 * 		Methods return null/false otherwise or if the information is unavailable. Defines Types from an XSD.
			 * define() : Define XML Schema as Types .
			 */
			List list = invoke(str , true).getXSDHelper().define(XMLUtils.toInputStream(xsdSchemas[i].getSource()) , null);
			
			for(int j = 0 ; j < list.size() ; ++j){
				Type type = (Type) list.get(j);  
				// getName(): Returns the name of the type . 
				if(!"DocumentRoot".equals(type.getName())){
					arrayList.add(type); 
					logger.debug("define TYPE from schema:{}" , type.getName());
					//logger.debug("define TYPE from schema:" );
				}
			}
		}
		
		invoke(str , xsdSchemas);
		
		return arrayList ; // [org.apache.tuscany.sdo.impl.ClassImpl@527a527a (name: ResponseHeaderType) (instanceClassName: null) (abstract: false, interface: false), org.apache.tuscany.sdo.impl.ClassImpl@65766576 (name: Add_TIA_CType) (instanceClassName: null) (abstract: false, interface: false), org.apache.tuscany.sdo.impl.ClassImpl@f500f5 (name: Add_TOA_CType) (instanceClassName: null) (abstract: false, interface: false), org.apache.tuscany.sdo.impl.ClassImpl@15471547 (name: queryAcctDate_CType) (instanceClassName: null) (abstract: false, interface: false), org.apache.tuscany.sdo.impl.ClassImpl@18191819 (name: queryAcctDateResponse_CType) (instanceClassName: null) (abstract: false, interface: false)]
	}

	
	public static DataObject createDataObjectDirectly(String str , QName qname){
		
		try {
			/*
			 * DataObject : A data object is a representation of some structured data . It is the fundamental component in the 
			 * 			SDO(Service Data Objects)package. Data objects support reflection, path-based accesss,convenience 
			 * 			creation and deletion methods , and the ablity to be part of a data graph .
			 * 				Each data object holds its data as a series of Properties . Properties can be accessed by name,
			 * 			property index , or using the property meta object itself. A data object can also contain references 
			 * 			to other data objects, through reference-type Properties .
			 * 				A data object has a series of convenience accessors for its Properties . These methods either use a 
			 * 			path(String),a property index, or the property's meta object itself, to identify the property . Some
			 * 			examples of the path-based accessors are as follows . 
			 * 
			 * getDataFactory():Gets the DataFactory to use in this context.
			 * DataFactory : A Factory for creating DataObjects . The created DataObjects are not connected to any other DataObjects .
			 * create(uri , typeName): Create a DataObject of the Type specified by typeName with the given package uri . 
			 */
			DataObject dataObject = invoke(str , false).getDataFactory().create(qname.getNamespaceURI() , qname.getLocalPart());
			return dataObject ;
			
		} catch (Exception e) {
			throw new IllegalArgumentException("No DataObject for {" + qname.getNamespaceURI() + "}" + qname.getLocalPart());
		}
		
	}
	
	public static DataObject createDataObject(String str , QName paramQName){
		
		try {
			
			QName qname = invoke(str , paramQName);
			
			DataObject dataObject = invoke(str , false).getDataFactory().create(qname.getNamespaceURI(), qname.getLocalPart());
			
			return dataObject ; 
			
		} catch (Exception e) {
			throw new IllegalArgumentException("No DataObject for {" + paramQName.getNamespaceURI() + "}" + paramQName.getLocalPart());
		}
		
	}
	 
	public static void save(String str , QName qname , DataObject dataObject , XMLStreamWriter xmlStreamWriter)
			throws IOException , XMLStreamException , TransformerException {
		
		if(dataObject == null){
			return ;
		}
		/*
		 * HelperContext提供了一套SDO助手类似实例的访问方法。它描述一个助手执行上下文。该接口中的方法返回的助手集对相同的SDO元数据都具有可见性，即它们在相同的“scope”中执行。
		 */
		HelperContext helperContext = invoke(str , false);
		/*
		 * getXMLHelper(): Gets the XMLHelper to use in this context . 
		 * XMLHelper : A helper to convert XML documents into DataObjects and DataObjects into XML documents .
		 * createDocument(DataObject,String,String): Creates an XML Document with the specified XML rootElement for the DataObject .
		 * 
		 * XMLDocument: Represents an XML Document containing a tree of DataObjects , An example XML Document fragment is ;
		 * 		created from this XML Schema fragment ;Upon loading this XML Document ;DataObject is an instance of Type PurchaseOrderType
		 * 		RootElmentURI is null because the XSD has no targetNamespace URI.RootElementName is purchaseOrder.Encoding
		 * 		is null because the document did not specify an encoding . XMLDeclaration is true because the document 
		 * 		contained an XML declaration .XML Version is 1.0 SchemaLocation and noNamespaceSchemaLocation are null
		 * 		because they are not specified in the document.When saving the root element.if the type of the root dataObject
		 * 		is not the type of global element specified by rootElementURI and rootElementName, or if a global element
		 * 		does not exist for rootElementURI and rootElementName , then an xsi:type declaration is writen to record the root
		 * 		DataObject's Type.When loading the root element and an xsi:type declaration is found it is used as the type of 
		 * 		the root DataObject.In this case,if validation is not being performed, It is not an error if the rootElementName
		 * 		is not a global element.
		 */
		XMLDocument xmlDocument = helperContext.getXMLHelper().createDocument(dataObject, qname.getNamespaceURI(), qname.getLocalPart());
		
		/*
		 * XMLStreamHelper : Helper interface for reading and writing SDO DataObjects from XML streams (StAX).
		 */
		XMLStreamHelper xmlStreamHelper = SDOUtil.createXMLStreamHelper(helperContext);
		/*
		 * save(XMLDocument,XMLStreamWriter): Save a XMLDocument to an XML stream .
		 */
		xmlStreamHelper.save(xmlDocument, xmlStreamWriter);
		
	}
	
	public static DataObject load(String str , XMLStreamReader xmlStreamReader) throws IOException , XMLStreamException {
		xmlStreamReader = new XMLDocumentStreamReader(xmlStreamReader);
		
		/*
		 * load(XMLStreamReader): Create a DataObject from an element in a XML stream .
		 */
		XMLDocument xmlDocument = SDOUtil.createXMLStreamHelper(invoke(str , false)).load(xmlStreamReader);
		
		return xmlDocument.getRootObject();
	}
}
